#include <array>
#include "vtkActor.h"
#include "vtkAxisActor.h"
#include "vtkCamera.h"
#include "vtkCoordinate.h"
#include "vtkCubeAxesActor.h"
#include "vtkPlaneSource.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkProperty2D.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include "vtkStructuredGrid.h"
#include "vtkTextProperty.h"

#include "vtkCubeAxesActor.h"

#include "vtkPolygon.h"


#include "vtkBYUReader.h"
#include "vtkCamera.h"
#include "vtkLODActor.h"
#include "vtkLight.h"
#include "vtkNew.h"
#include "vtkOutlineFilter.h"
#include "vtkPolarAxesActor.h"
#include "vtkPolyDataMapper.h"
#include "vtkPolyDataNormals.h"
#include "vtkProperty.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include "vtkTextProperty.h"

#include "vtkTestUtilities.h"
#include "vtkLookupTable.h"
#include "vtkScalarBarActor.h"
#include "vtkNamedColors.h"
#include "vtkColorTransferFunction.h"
#include "vtkTextActor.h"
#include "vtkTextMapper.h"

#include <vtkAnnotatedCubeActor.h>
#include <vtkAxesActor.h>
#include <vtkBandedPolyDataContourFilter.h>
#include <vtkCaptionActor2D.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkTextMapper.h>
#include <vtkTextActor3D.h>
#include <vtkVectorText.h>
#include <vtkFollower.h>

int TestPolarAxes(int argc, char* argv[]);
vtkSmartPointer<vtkActor> createRing();
vtkSmartPointer<vtkLookupTable> GetDivergingLut();


int main(int argc, char* argv[])
{
    return TestPolarAxes(argc, argv);

    return EXIT_SUCCESS;
}



vtkSmartPointer<vtkActor> createRing() {

    const int RowCount = 36;
    const int ColCount = 40;
    const double RowDegree = vtkMath::Pi() * 2 / RowCount;
    const double Radius = 500;
    const int AreaAvgRadius = Radius / ColCount;
    const int ArcSegmentCount = 1;
    double ArcSegmentDegree = RowDegree / ArcSegmentCount;

    vtkNew<vtkCellArray> allPolygons;
    vtkNew<vtkPoints> allPoints;
    vtkNew<vtkPolyData> polygonPolyData;

    for (int i = 0; i < RowCount; ++i) {

        double degree = RowDegree * i;

        for (int j = 0; j < ColCount; ++j) {

            vtkNew<vtkPolygon> polygon;
            polygon->GetPointIds()->SetNumberOfIds((ArcSegmentCount + 1) * 2);
            vtkIdType numberOfPoints = allPoints->GetNumberOfPoints();

            double radius0 = j * AreaAvgRadius;
            double radius1 = (j + 1) * AreaAvgRadius;

            for (int k = 0; k < ArcSegmentCount + 1; ++k) {
                double d0 = degree + (ArcSegmentDegree) * k;
                double x0 = cos(d0) * radius0;
                double y0 = sin(d0) * radius0;
                allPoints->InsertNextPoint(x0, y0, 0.0);

                double d1 = degree + (ArcSegmentDegree) * k;
                double x1 = cos(d1) * radius1;
                double y1 = sin(d1) * radius1;
                allPoints->InsertNextPoint(x1, y1, 0.0);

                polygon->GetPointIds()->SetId(k, numberOfPoints + (ArcSegmentCount - k));
                polygon->GetPointIds()->SetId(k + ArcSegmentCount + 1, numberOfPoints + ArcSegmentCount + 1 + k);
            }

            allPolygons->InsertNextCell(polygon);
        }
    }

    polygonPolyData->SetPoints(allPoints);
    polygonPolyData->SetPolys(allPolygons);


//    vtkNew<vtkPolyDataMapper2D> mapper;
//    mapper->SetInputData(polygonPolyData);
//    vtkSmartPointer<vtkCoordinate> normCoords = vtkSmartPointer<vtkCoordinate>::New();
//    normCoords->SetCoordinateSystemToView();
//    mapper->SetTransformCoordinate( normCoords );

//    vtkNew<vtkActor2D> actor;
//    actor->SetMapper(mapper);
////    actor->GetProperty()->EdgeVisibilityOn();
//    actor->GetProperty();
//    renderer->AddActor2D(actor);


//    vtkColor4ub(100, 0,  100).GetData();
    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputData(polygonPolyData);

    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);
    actor->GetProperty()->EdgeVisibilityOn();
    actor->GetProperty();

    return actor;
}


//----------------------------------------------------------------------------
int TestPolarAxes(int argc, char* argv[])
{

    vtkNew<vtkCamera> camera;
    camera->SetClippingRange(1., 100.);
    camera->SetFocalPoint(0., .5, 0.);
    camera->SetPosition(5., 6., 14.);

    vtkNew<vtkLight> light;
    light->SetColor(0, 1.0, 0);
    light->SetFocalPoint(0.21406, 1.5, 0.0);
    light->SetPosition(7., 7., 4.);

    vtkNew<vtkRenderer> renderer;
    renderer->SetActiveCamera(camera);
    renderer->AddLight(light);


    vtkNew<vtkPolarAxesActor> polaxes;
//    polaxes->SetBounds(normals->GetOutput()->GetBounds());
//    polaxes->SetBounds(-500, 500, -500, 500, 0, 0);
//    polaxes->SetPole(.5, 1., 3.);
//    polaxes->SetMaximumRadius(3.);
//    polaxes->SetMaximumRadius(3.);
//    polaxes->SetMinimumAngle(-60.);
//    polaxes->SetMaximumAngle(210.);
    polaxes->SetMinimumAngle(0.);
    polaxes->SetMaximumAngle(360.);
    polaxes->SetRange(0, 50);
//    polaxes->SetRequestedNumberOfRadialAxes(10);
    polaxes->SetRequestedNumberOfRadialAxes(13);
    polaxes->SetCamera(renderer->GetActiveCamera());
    polaxes->SetPolarLabelFormat("%6.1fkm");
    polaxes->GetLastRadialAxisProperty()->SetColor(.0, .0, 1.);
    polaxes->GetSecondaryRadialAxesProperty()->SetColor(.0, .0, 1.);
//    polaxes->GetPolarArcsProperty()->SetColor(1., .0, 0.);
//    polaxes->GetSecondaryPolarArcsProperty()->SetColor(1., 1., 1.);
//    polaxes->GetPolarAxisProperty()->SetColor(.2, .2, .2);
//    polaxes->GetPolarAxisTitleTextProperty()->SetColor(1, 1, 0);
//    polaxes->GetPolarAxisLabelTextProperty()->SetColor(0, 1, 1);

    polaxes->GetPolarAxisTitleTextProperty()->SetOpacity(false);
//    polaxes->GetPolarAxisLabelTextProperty()->SetOpacity(false);

//    polaxes->SetPolarLabelVisibility(false);
    polaxes->SetRadialUnits(false);
    polaxes->RadialTitleVisibilityOff();

    polaxes->GetSecondaryRadialAxesTextProperty()->SetColor(1, 0, 1);
    polaxes->SetNumberOfPolarAxisTicks(4);
    polaxes->SetAutoSubdividePolarAxis(false);
    polaxes->SetScreenSize(10.0);

    polaxes->SetUse2DMode(1);


    std::cout << polaxes->GetBounds()[0] << "-" \
     << polaxes->GetBounds()[1] << "-" \
      << polaxes->GetBounds()[2] << "-" \
       << polaxes->GetBounds()[3] << "-" \
        << polaxes->GetBounds()[4] << "-" \
         << polaxes->GetBounds()[5] << "-";



    // Create a lookup table that consists of the full hue circle
    // (from HSV).
    vtkNew<vtkNamedColors> colors;
    auto belowRangeColor = colors->GetColor4d("Black").GetData();
    belowRangeColor[3] = 0.2;
    auto aboveRangeColor = colors->GetColor4d("White").GetData();
    aboveRangeColor[3] = 0.2;
    vtkNew<vtkLookupTable> hueLut;
    int radius = 100;
    hueLut->SetTableRange(-radius, radius);
    hueLut->SetHueRange(0.667, 0);
    hueLut->SetSaturationRange(1, 1);
    hueLut->SetValueRange(1, 1);
    hueLut->UseBelowRangeColorOn();
    hueLut->SetBelowRangeColor(belowRangeColor);
    hueLut->UseAboveRangeColorOn();
    hueLut->SetAboveRangeColor(aboveRangeColor);
    hueLut->SetNumberOfColors(5);
    hueLut->Build();
    double* last = hueLut->GetTableValue(4);
    hueLut->SetAboveRangeColor(last[0], last[1], last[2], 0);

    // Create a scalar bar
    vtkNew<vtkScalarBarActor> scalarBar;
//    scalarBar->SetLookupTable(hueLut);
    scalarBar->SetLookupTable(GetDivergingLut());
    scalarBar->SetTitle("Distance");
    scalarBar->SetNumberOfLabels(16);

    vtkNew<vtkTextProperty> textProperty;
    textProperty->SetFontSize(15);
    textProperty->SetJustificationToCentered();
    scalarBar->SetLabelTextProperty(textProperty);
    scalarBar->SetAnnotationTextProperty(textProperty);
    scalarBar->SetTitleTextProperty(textProperty);

    scalarBar->UnconstrainedFontSizeOn();
//    scalarBar->SetNumberOfLabels(5);
//    scalarBar->SetMaximumWidthInPixels(100);
//    scalarBar->SetMaximumHeightInPixels(100);
    scalarBar->SetLabelFormat("%.0f");

//    scalarBar->SetOrientationToHorizontal();
//    scalarBar->SetWidth(0.8);

    scalarBar->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
    scalarBar->GetPositionCoordinate()->SetValue(0.9, 0.1);
    scalarBar->SetHeight(0.8);

    renderer->AddActor2D(scalarBar);


//    vtkNew<vtkTextActor> title;
    vtkNew<vtkTextMapper> titleMapper;
//    titleMapper->SetInput("Hello World!");
    std::string title = "Hello World!";
    titleMapper->SetInput(title.c_str());
    vtkNew<vtkTextProperty> titleProperty;
    titleProperty->SetFontSize(30);
    titleProperty->SetJustificationToCentered();
    titleMapper->SetTextProperty(titleProperty);
    vtkNew<vtkActor2D> titleActor;
    titleActor->SetMapper(titleMapper);
//    titleActor->SetPosition(50, 10);
//    titleActor->SetDisplayPosition(0.5, 0.5);

    titleActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
    titleActor->GetPositionCoordinate()->SetValue(0.5, 0.9);
    renderer->AddViewProp(titleActor);


    vtkNew<vtkRenderWindow> renWin;
    renWin->SetMultiSamples(0);
    renWin->AddRenderer(renderer);
    renWin->SetWindowName("VTK - Polar Axes");
    renWin->SetSize(600, 600);

    vtkNew<vtkRenderWindowInteractor> iren;
    iren->SetRenderWindow(renWin);


// 测试文字




//    vtkNew<vtkTextProperty> titleProperty2;
//    titleProperty2->SetFontSize(10);
//    titleProperty2->SetJustificationToCentered();
//
//    vtkNew<vtkTextActor3D> titleActor2;
//    titleActor2->SetInput("asdfasdfasdfasdfsdf");
//    titleActor2->SetTextProperty(titleProperty2);
//    titleActor2->SetScale(0.5, 0.5, 0.5);
//
////    titleActor2->SetMapper(titleMapper2);
//    titleActor2->SetPosition(2, 2, 2);
//    renderer->AddActor(titleActor2);



    vtkNew<vtkVectorText> TextSrc;
    TextSrc->SetText("asdfasdfasdfs1111");

    vtkNew<vtkPolyDataMapper> LabelMapper;
    LabelMapper->SetInputConnection(TextSrc->GetOutputPort());

    vtkNew<vtkFollower> LabelActor;
    LabelActor->SetMapper(LabelMapper);
    LabelActor->SetPosition(0, 0, 0);
    LabelActor->SetScale(1, 1, 1);
    LabelActor->SetOrigin(TextSrc->GetOutput()->GetCenter());
    LabelActor->GetProperty()->SetColor(1, 0, 0);

    renderer->AddActor(LabelActor);
    LabelActor->SetCamera(renderer->GetActiveCamera());

    // 测试

    std::array<std::string, 3> xyzLabels{{"X", "Y", "Z"}};
    std::array<double, 3> scale{{1.0, 1.0, 1.0}};

    vtkNew<vtkAxesActor> axes111;
    axes111->SetScale(scale[0], scale[1], scale[2]);
    axes111->SetShaftTypeToCylinder();
    axes111->SetXAxisLabelText(xyzLabels[0].c_str());
    axes111->SetYAxisLabelText(xyzLabels[1].c_str());
    axes111->SetZAxisLabelText(xyzLabels[2].c_str());
    axes111->SetCylinderRadius(0.5 * axes111->GetCylinderRadius());
    axes111->SetConeRadius(1.025 * axes111->GetConeRadius());
    axes111->SetSphereRadius(1.5 * axes111->GetSphereRadius());
    vtkTextProperty* tprop = axes111->GetXAxisCaptionActor2D()->GetCaptionTextProperty();
    tprop->ItalicOn();
    tprop->ShadowOn();
    tprop->SetFontFamilyToTimes();
    // Use the same text properties on the other two axes.
    axes111->GetYAxisCaptionActor2D()->GetCaptionTextProperty()->ShallowCopy(tprop);
    axes111->GetZAxisCaptionActor2D()->GetCaptionTextProperty()->ShallowCopy(tprop);

    vtkNew<vtkOrientationMarkerWidget> om2;
    om2->SetOrientationMarker(axes111);
    // Position lower right in the viewport.
    om2->SetViewport(0.8, 0, 1.0, 0.2);
//    om2->SetViewport(0.5, 0.5, 1.0, 0.2);
    om2->SetInteractor(iren);
    om2->EnabledOn();
//    om2->InteractiveOn();
//    this->win->GetInteractor()->Render();


    renderer->SetBackground(.8, .8, .8);
    renderer->AddViewProp(polaxes);
    renWin->Render();

    iren->Start();

    return EXIT_SUCCESS;
}


vtkSmartPointer<vtkLookupTable> GetDivergingLut()
{

//    vtkNew<vtkColorTransferFunction> ctf;
//    ctf->SetColorSpaceToDiverging();
////    ctf->AddRGBPoint(0.0, 0.230, 0.299, 0.754);
////    ctf->AddRGBPoint(0.5, 0.865, 0.865, 0.865);
////    ctf->AddRGBPoint(1.0, 0.706, 0.016, 0.150);
//
//    ctf->AddRGBPoint(0, 92, 201, 250);
//    ctf->AddRGBPoint(1.0, 117, 20, 114);


    vtkNew<vtkColorTransferFunction> colorTransferFunction;
    colorTransferFunction->SetColorSpaceToRGB();
    std::vector<std::array<double, 3>> colorList;
    colorList.push_back({92, 201, 250});
    colorList.push_back({42, 100, 246});
    colorList.push_back({17, 50, 196});
    colorList.push_back({117, 251, 122});
    colorList.push_back({102, 201, 113});
    colorList.push_back({67, 151, 42});
    colorList.push_back({254, 255, 127});
    colorList.push_back({247, 206, 85});
    colorList.push_back({241, 158, 56});
    colorList.push_back({236, 112, 107});
    colorList.push_back({234, 72, 63});
    colorList.push_back({186, 38, 26});
    colorList.push_back({234, 51, 247});
    colorList.push_back({188, 39, 198});
    colorList.push_back({117, 20, 124});

    double rangeMin = 0;
    double rangeMax = 75;
    int colorCount = colorList.size();
    double rangeSection = (rangeMax - rangeMin) / colorCount;
    for (int i = 0; i < colorCount; ++i) {
        std::array<double, 3> color = colorList[i];
        colorTransferFunction->AddRGBPoint(rangeSection * i, color[0] / 255.0, color[1] / 255.0, color[2] / 255.0);
        if (i < colorCount - 1) {
            std::array<double, 3> color1 = colorList[i + 1];
            colorTransferFunction->AddRGBPoint(rangeSection * (i + 1) - 0.01, color1[0] / 255.0, color1[1] / 255.0, color1[2] / 255.0);
        }
    }

    colorTransferFunction->Build();

    auto tableSize = 15;
    vtkNew<vtkLookupTable> lut;
    lut->SetVectorModeToRGBColors();
    lut->SetNumberOfTableValues(tableSize);
    lut->SetTableRange(0, 75);
    lut->Build();

    for (auto i = 0; i < lut->GetNumberOfColors(); ++i)
    {
        std::array<double, 3> rgb;
        colorTransferFunction->GetColor(static_cast<double>(i) / lut->GetNumberOfColors(),
                      rgb.data());
        colorTransferFunction->GetColor(static_cast<double>(i) * 5, rgb.data());

//        std::cout << i << ":" << rgb[0] << " - " << rgb[1] << " - " << rgb[2] << std::endl;

//        std::array<double, 4> rgb;
//        if (i == 0) {
//            rgb = {1, 0, 0, 1};
//        }else {
//            rgb = {0, 1, 0, 1};
//        }
//
//        std::cout << i << ":" << rgb.data()[0] << " - " << rgb.data()[1] << " - " << rgb.data()[2] << std::endl;
//
//        lut->SetTableValue(static_cast<vtkIdType>(i), rgb.data());

        std::array<double, 4> rgba{0.0, 0.0, 0.0, 1.0};
        std::copy(std::begin(rgb), std::end(rgb), std::begin(rgba));
        lut->SetTableValue(static_cast<vtkIdType>(i), rgba.data());
    }

    return lut;
}